/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.hex;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

class Measures {
    private HexEditor hex;
    private int headerChars;
    private int cellChars;
    private int headerWidth;
    private int spacerWidth;
    private int cellWidth;
    private int cellHeight;
    private int cols;
    private int baseX;
    private boolean guessed;
    
    public Measures(HexEditor hex) {
        this.hex = hex;
        this.guessed = true;
        this.cols = 1;
        this.cellWidth = -1;
        this.cellHeight = -1;
        this.cellChars = 2;
        this.headerChars = 4;
        
        computeCellSize(null);
    }
    
    public int getColumnCount() { return cols; }
    public int getBaseX() { return baseX; }
    public int getCellHeight() { return cellHeight; }
    public int getCellWidth() { return cellWidth; }
    public int getLabelWidth() { return headerWidth; }
    public int getLabelChars() { return headerChars; }
    public int getCellChars() { return cellChars; }
    
    public int getValuesX() {
        return baseX + spacerWidth;
    }
    
    public int getValuesWidth() {
        return ((cols - 1) / 4) * spacerWidth + cols * cellWidth;
    }
    
    public long getBaseAddress(HexModel model) {
        if(model == null) {
            return 0;
        } else {
            long addr0 = model.getFirstOffset();
            return addr0 - addr0 % cols;
        }
    }

    public int toY(long addr) {
        long row = (addr - getBaseAddress(hex.getModel())) / cols;
        long ret = row * cellHeight;
        return ret < Integer.MAX_VALUE ? (int) ret : Integer.MAX_VALUE;
    }
    
    public int toX(long addr) {
        int col = (int) (addr % cols);
        return baseX + (1 + (col / 4)) * spacerWidth + col * cellWidth;
    }

    public long toAddress(int x, int y) {
        HexModel model = hex.getModel();
        if(model == null) return Integer.MIN_VALUE;
        long addr0 = model.getFirstOffset();
        long addr1 = model.getLastOffset();

        long base = getBaseAddress(model) + ((long) y / cellHeight) * cols;
        int offs = (x - baseX) / (cellWidth + (spacerWidth + 2) / 4);
        if(offs < 0) offs = 0;
        if(offs >= cols) offs = cols - 1;
        
        long ret = base + offs;
        if(ret > addr1) ret = addr1;
        if(ret < addr0) ret = addr0;
        return ret;
    }
    
    void ensureComputed(Graphics g) {
        if(guessed || cellWidth < 0) computeCellSize(g);
    }

    void recompute() {
        computeCellSize(hex.getGraphics());
    }
    
    void widthChanged() {
        int oldCols = cols;
        int width;
        if(guessed || cellWidth < 0) {
            cols = 16;
            width = hex.getPreferredSize().width;
        } else {
            width = hex.getWidth();
            int ret = (width - headerWidth) / (cellWidth + (spacerWidth + 3) / 4);
            if(ret >= 16) cols = 16;
            else if(ret >= 8) cols = 8;
            else cols = 4;
        }
        int lineWidth = headerWidth + cols * cellWidth
            + ((cols / 4) - 1) * spacerWidth;
        int newBase = headerWidth + Math.max(0, (width - lineWidth) / 2);
        if(baseX != newBase) {
            baseX = newBase;
            hex.repaint();
        }
        if(cols != oldCols) recompute();
    }
    
    private void computeCellSize(Graphics g) {
        HexModel model = hex.getModel();
        
        // compute number of characters in headers and cells
        if(model == null) {
            headerChars = 4;
            cellChars = 2;
        } else {
            int logSize = 0;
            long addrEnd = model.getLastOffset();
            while(addrEnd > (1L << logSize)) {
                logSize++;
            }
            headerChars = (logSize + 3) / 4;
            cellChars = (model.getValueWidth() + 3) / 4;
        }

        // compute character sizes
        FontMetrics fm = g == null ? null : g.getFontMetrics(hex.getFont());
        int charWidth;
        int spaceWidth;
        int lineHeight;
        if(fm == null) {
            charWidth = 8;
            spaceWidth = 6;
            Font font = hex.getFont();
            if(font == null) {
                lineHeight = 16;
            } else {
                lineHeight = font.getSize();
            }
        } else {
            guessed = false;
            charWidth = 0;
            for(int i = 0; i < 16; i++) {
                int width = fm.stringWidth(Integer.toHexString(i));
                if(width > charWidth) charWidth = width;
            }
            spaceWidth = fm.stringWidth(" ");
            lineHeight = fm.getHeight();
        }

        // update header and cell dimensions
        headerWidth = headerChars * charWidth + spaceWidth;
        spacerWidth = spaceWidth;
        cellWidth = cellChars * charWidth + spaceWidth;
        cellHeight = lineHeight;

        // compute preferred size
        int width = headerWidth + cols * cellWidth + (cols / 4) * spacerWidth;
        long height;
        if(model == null) {
            height = 16 * cellHeight;
        } else {
            long addr0 = getBaseAddress(model);
            long addr1 = model.getLastOffset();
            long rows = (int) (((addr1 - addr0 + 1) + cols - 1) / cols);
            height = rows * cellHeight;
            if(height > Integer.MAX_VALUE) height = Integer.MAX_VALUE;
        }

        // update preferred size
        Dimension pref = hex.getPreferredSize();
        if(pref.width != width || pref.height != height) {
            pref.width = width;
            pref.height = (int) height;
            hex.setPreferredSize(pref);
            hex.revalidate();
        }
        
        widthChanged();
    }
}
